package com.boco.blch.runner.executor;

import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.log4j.Logger;

import com.boco.blch.runner.result.Result;
import com.boco.blch.runner.result.ResultInfo;
import com.boco.blch.runner.util.ErrorMessageUtil;
import com.boco.blch.runner.util.ObjectUtil;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.Connection;
import com.trilead.ssh2.SFTPv3Client;
import com.trilead.ssh2.SFTPv3FileHandle;
import com.trilead.ssh2.Session;
import com.trilead.ssh2.StreamGobbler;

public abstract class AbstractExecutor implements Executor{
	protected final static int BUFFER_SIZE_MEDIUM = 8192;
	protected final static int BUFFER_SIZE_MAX = 65536;
	protected final static int DEFAULT_BLOCK_TIME = 1000;
	public final static String EOF = "##!#HERE_IS_THE_END_OF_THIS_SCRIPT#!##";
	
	/*脚本退出标志*/
	public final static ConcurrentLinkedQueue<String> QUIT_FLAG_ARRAY = new ConcurrentLinkedQueue<String>();
	static{
		QUIT_FLAG_ARRAY.add(EOF);
		QUIT_FLAG_ARRAY.add("root's password");
	}
	
	private static Logger logger = Logger.getLogger(AbstractExecutor.class);
	protected Connection conn;
	
	protected void close(Session sess){
		if(sess != null){
			sess.close();
		}
	}
	
	protected String getSuccessInfoFromSession(Session sess) {
		return getMessageFromInputStream(sess.getStdout());
	}
	protected String getErrorInfoFromSession(Session sess) {
		return getMessageFromInputStream(sess.getStderr());
	}
	
	private String getMessageFromInputStream(InputStream is){
		StringBuilder sb = new StringBuilder();
		InputStream inputstream = new StreamGobbler(is);
		@SuppressWarnings("resource")
		BufferedReader br = new BufferedReader(new InputStreamReader(inputstream));
		
		while (true){
			String line = null;
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (line == null)
				break;
			sb.append(line);
			sb.append(System.getProperty("line.separator"));
		}
		return sb.toString();
	}
	
	protected void delShellScriptBySFTPv3(String filename, Connection conn){
		SFTPv3Client client = null;
		try{
			client = new SFTPv3Client(conn);
			client.rm(filename);
		} catch (Exception e){
			logger.error("delete script file failed! " + e);
		} finally {
			if(client != null)
				client.close();
		}
	}
	
	protected void createShellScriptBySFTPv3(String filename, String command){
		SFTPv3Client client = null;
		try{
			client = new SFTPv3Client(conn);
			SFTPv3FileHandle handler = client.createFile(filename);
			byte[] src = command.getBytes();
			client.write(handler, 0L, src, 0, src.length);
		} catch (Exception e){
			logger.error("create script file failed! " + e);
		} finally {
			if(client != null)
				client.close();
		}
	}
	
	protected void createShellScriptBySFTPv3(String filename, String command, Connection conn){
		SFTPv3Client client = null;
		try{
			client = new SFTPv3Client(conn);
			SFTPv3FileHandle handler = client.createFile(filename);
			byte[] src = command.getBytes();
			client.write(handler, 0L, src, 0, src.length);
		} catch (Exception e){
			logger.error("create script file failed! " + e);
		} finally {
			if(client != null)
				client.close();
		}
	}
	
	/* 执行脚本的指令 */
	protected String execSh(String filename) {
		return "./" + filename;
	}
	
	/* 修改脚本执行权限的指令 */
	protected String chmod(String filename) {
		return "chmod +x " + filename;
	}
	
	/* 发送关闭session的标志*/
	protected void eofFlag(OutputStream os){
		try {
			os.write(("echo '" + EOF + "'").getBytes());
			os.write(KeyEvent.VK_ENTER);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	
	/**
	 * 调用startShell方法，以模拟终端的模式执行指令。
	 * @deprecated
	 * @return
	 */
	protected Result executeAsTerminal(Session session, String command) {
		String filename = ObjectUtil.filenameGenerator("sh");
		OutputStream os = null;
		try {
			os = session.getStdin();
			//创建脚本
			createShellScriptBySFTPv3(filename, command);
			
			try{
				session.requestPTY("dumb", 90, 30, 0, 0, null);
				session.startShell();
				//等待session连接。否则下面的修改权限等命令可能会空跑，导致无法退出流
				session.waitForCondition(ChannelCondition.STDOUT_DATA, Long.MAX_VALUE);
			} catch(Exception e){
				
			}

			StdoutThread successInfoThread = new StdoutThread(session);
			Thread successThread = new Thread(successInfoThread);

			//修改权限，执行脚本
			os.write(chmod(filename).getBytes());os.write(KeyEvent.VK_ENTER);
			os.write(execSh(filename).getBytes());os.write(KeyEvent.VK_ENTER);
				
			successThread.start();
			
			successThread.join();
			// 返回结果
			Result sResult = successInfoThread.getResult();
			
			return sResult;
		} catch (IOException e) {
			return new ResultInfo(false, 128, e.getMessage());
		} catch (InterruptedException e) {
			return new ResultInfo(false, 129, ErrorMessageUtil.getErrorMessageByErrorCode(129));
		} finally {
			try {
				if(os != null){
					os.close();
				}
				//删除脚本
				delShellScriptBySFTPv3(filename, conn);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	protected Result _executeAsTerminal(Session session, String command) {
		session.waitForCondition(ChannelCondition.EOF, 200);
		String filename = ObjectUtil.filenameGenerator("sh");
		try {
			OutputStream os = session.getStdin();
			//创建脚本
			createShellScriptBySFTPv3(filename, command);
			
			StdoutThread successInfoThread = new StdoutThread(session);
			Thread successThread = new Thread(successInfoThread);

			//修改权限，执行脚本
			os.write(chmod(filename).getBytes());os.write(KeyEvent.VK_ENTER);
			os.write(execSh(filename).getBytes());os.write(KeyEvent.VK_ENTER);
				
			successThread.start();
			
			successThread.join();
			// 返回结果
			Result sResult = successInfoThread.getResult();
			
			return sResult;
		} catch (IOException e) {
			return new ResultInfo(false, 128, e.getMessage());
		} catch (InterruptedException e) {
			return new ResultInfo(false, 129, ErrorMessageUtil.getErrorMessageByErrorCode(129));
		} finally {
			delShellScriptBySFTPv3(filename, conn);
		}
	}
	
	
	/**
	 * 获取回显线程
	 * 
	 * @author someone
	 *
	 */
	class StdoutThread implements Runnable {
		private Result result;
		private Session session;
		
		public StdoutThread(Session session){
			this.session = session;
		}
		public Result getResult() {
			return result;
		}
		
		/**
		 * 判断脚本是否应该退出执行
		 * @param line
		 * @return
		 */
		private boolean shouldQuit(String line){
			for(String quitFlag : QUIT_FLAG_ARRAY){
				if(line.contains(quitFlag))
					return true;
			}
			return false;
		}

		@Override
		public void run() {

			//InputStream br = new StreamGobbler(session.getStdout());
			InputStream br = session.getStdout();
			int len = 0;
			byte[] buff = new byte[BUFFER_SIZE_MAX];
			try {
				StringBuilder sb = new StringBuilder();
				String line = "";

				while (true) {
					try {
						len = br.read(buff);
						line = new String(buff, 0, len, "UTF-8");
						sb.append(line);
						if(shouldQuit(line)){
							break;
						}
					} catch (Exception e) {
						break;
					}
				}

				result = new ResultInfo(true, 0, sb.toString());
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				try {
					if (br != null)
						br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * 获取错误信息回显线程
	 * 
	 * @author someone
	 *
	 */
	class StderrThread implements Runnable {
		private Result result;
		private Session session;
		
		public StderrThread(Session session){
			this.session = session;
		}
		public Result getResult() {
			return result;
		}

		@Override
		public void run() {
			InputStream br = session.getStderr();
			int len = 0;
			byte[] buff = new byte[BUFFER_SIZE_MEDIUM];
			try {
				StringBuilder sb = new StringBuilder();
				String line = null;
				while (true) {
					try {
						len = br.read(buff);
						line = new String(buff, 0, len);
						sb.append(line);
						if(line.contains(EOF)){
							break;
						}
					} catch (Exception e) {
						break;
					}
				}
				result = new ResultInfo(false, -1, sb.toString());
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (br != null)
						br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * 杀手线程，定时关闭session
	 * @author someone
	 *
	 */
	class Killer implements Runnable {
		private Session session;
		
		long timeout;

		public Killer(Session session, long timeout) {
			this.timeout = timeout;
			this.session = session;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(timeout);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			session.close();
		}
	}

	@Override
	public abstract Result execute();
	
}
